package com.example.proxy.extra;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;

import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class MappingScanService  implements ApplicationContextAware {
    private ApplicationContext applicationContext;
    private List<MappingClass> list = new ArrayList<>();
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
        try{
            scan();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    public void scan(){
        Map<String,String> maps = new HashMap<>();
        Map<String,Object> map = this.applicationContext.getBeansWithAnnotation(RequestMapping.class);
        for(Map.Entry<String,Object> entry : map.entrySet()){
            Class<?> clazz = entry.getValue().getClass();
            String packageName = clazz.getPackage().getName();
            if(!packageName.startsWith("com.example.proxy.controller")){
                continue;
            }
            RequestMapping requestMappingClassAnno = clazz.getAnnotation(RequestMapping.class);
            if(requestMappingClassAnno == null){
                requestMappingClassAnno = AnnotationUtils.findAnnotation(clazz,RequestMapping.class);
            }
            if(requestMappingClassAnno == null){
                clazz = clazz.getSuperclass();
                requestMappingClassAnno = AnnotationUtils.findAnnotation(clazz,RequestMapping.class);
            }
            if(requestMappingClassAnno == null){
                continue;
            }
            String[] classMappings = requestMappingClassAnno.value();
            Method[] methods = clazz.getMethods();
            for(Method m : methods){
                RequestMapping mappingMethodAnno = AnnotationUtils.findAnnotation(m,RequestMapping.class);
                if(mappingMethodAnno == null){
                    continue;
                }
                String[] methodMappings = mappingMethodAnno.value();
                compose(classMappings,methodMappings);
            }

        }
    }

    private void compose(String[] classMappings, String[] methodMappings) {
        if(classMappings.length <= 0 || methodMappings.length <= 0){
            return;
        }
        char changed = (classMappings.length > 1|| methodMappings.length > 1)?'Y':'N';
        String originStr = classMappings[0] + formatStart(methodMappings[0]);
        String newStr = classMappings[classMappings.length - 1] +
                formatStart(methodMappings[methodMappings.length - 1]);
        this.list.add(new MappingClass(originStr,newStr,changed));
    }

    private String formatStart(String methodMapping) {
        if(methodMapping.startsWith("/"))return methodMapping;
        return "/"+methodMapping;
    }
    public List<MappingClass> getAll(){
        return list;
    }
    public List<MappingClass> filter(String oldValue,String newValue,char changed){
        boolean notmatchOld = StringUtils.isEmpty(oldValue);
        boolean notmatchNew = StringUtils.isEmpty(newValue);
        return list.stream().filter(x -> {
            return (notmatchOld || x.getOldValue().contains(oldValue))
                    && (notmatchNew || x.getNewValue().contains(newValue ))
                    && (x.getChanged() == changed);
        }).map(x -> {
            MappingClass mc = new MappingClass();
            if(!notmatchOld){
                mc.setOldValue(x.getOldValue().replaceAll(oldValue,"<span>"+oldValue+"</span>"));
            }else {
                mc.setOldValue(x.getOldValue());
            }
            if(!notmatchNew){
                mc.setNewValue(x.getNewValue().replaceAll(newValue,"<sapn>"+newValue+"</span>"));
            }else {
                mc.setNewValue(x.getNewValue());
            }
            mc.setChanged(x.getChanged());
            return mc;
        }).collect(Collectors.toList());
    }
}
